/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *	  http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.milib.display.service {
	import com.googlecode.milib.base.MLBroadcastClass;
	import com.googlecode.milib.display.ui.scroller.IScrollable;
	import com.googlecode.milib.service.singleton.KeyboardService;
	import com.googlecode.milib.service.singleton.LinksFromMLTextFieldGlobalBroadcaster;
	import com.googlecode.milib.service.singleton.MouseService;
	import com.googlecode.milib.vo.MLEvent;

	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.TextEvent;
	import flash.text.TextField;
	import flash.utils.Dictionary;

	/**
	 * @author Marek Brun
	 */
	public class MLTextField extends MLBroadcastClass implements IScrollable {
		
		//Dispatched after a control's value is modified.
		//DATA:	Event
		public const event_Change : String = 'Change';
		
		//Dispatched when a user clicks a hyperlink in an HTML-enabled
		//text field, where the URL begins with "event:".
		//DATA:	TextEvent
		public const event_Link : String = 'Link';
		
		//Dispatched by a TextField object after the user scrolls.
		//DATA:	Event
		public const event_Scroll : String = 'Scroll';
		
		//Flash Player dispatches the textInput event when a user enters one or more characters of text.
		//DATA:	TextEvent
		public const event_BeforeTextInput : String = 'BeforeTextInput';		public const event_AfterTextInput : String = 'AfterTextInput';
		
		//invoked when a button loses keyboard focus
		//DATA: newFocus:Object
		public const event_FocusIn:String='FocusIns';
		
		//invoked when a button receives keyboard focus
		//DATA: oldFocus:Object
		public const event_FocusOut:String='FocusOut';
		
		//invoked when a button receives keyboard focus
		//DATA: oldFocus:Object
		public const event_MouseFocusChange:String='MouseFocusChange';
				public const event_FinishSelectingText:String='FinishSelectingText';
				public const event_MouseUpWhenFocus:String='MouseUpWhenFocus';		public const event_KeyUpWhenFocus:String='KeyUpWhenFocus';
		
		
		private var _tf:TextField;
		private static const servicedObjects : Dictionary = new Dictionary(true);
		private var gotFocus:Boolean;
		private var ms:MouseService;
		public var lastInput:String;
		public var lastTextBeforeInput:String='';
		public var lastCaretIndexBeforeInput:int=0;
		public var lastCaretIndex:int=0;
		public var lastSelectionBeginIndex:int=0;
		public var lastSelectionEndIndex:int=0;
		public var isInput:Boolean=false;
		private var km:KeyboardService;

		public function get event_ScrollChanged():String { return 'ScrollChanged'; }
		
		public function MLTextField(access:Private, tf:TextField) {
			_tf=tf;
			tf.addEventListener(Event.CHANGE, onTextFieldChange, false, 0, true);			tf.addEventListener(TextEvent.LINK, onTextFieldLink, false, 0, true);			tf.addEventListener(Event.SCROLL, onTextFieldScroll, false, 0, true);			tf.addEventListener(TextEvent.TEXT_INPUT, onTextFieldTextInput, false, 0, true);			tf.addEventListener(FocusEvent.FOCUS_IN, onTextFieldFocusIn, false, 0, true);			tf.addEventListener(FocusEvent.FOCUS_OUT, onTextFieldFocusOut, false, 0, true);			tf.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, onTextFieldMouseFocusChange, false, 0, true);
			
			ms=MouseService.getInstance();
			km=KeyboardService.getInstance();
		}

		public function get tf():TextField { return _tf; }
		
		public function getScroll():Number {
			if(tf.scrollV==1){ return 0; }
			return (tf.scrollV-1)/(tf.maxScrollV-1);
		}
		
		/**
		 * @param scroll value from 0 to 1
		 */
		public function setScroll(scroll:Number):void {
			tf.scrollV=Math.round(1+(tf.maxScrollV-1)*scroll);
		}
		
		public function setScrollByOneUnit(isUp:Boolean):void {
			tf.scrollV=tf.scrollV+ (isUp ? 1 : -1);
		}
		
		/**
		 * @return returns dependence of visible content (eg. text) to total length (visible/total)
		 */
		public function getVisibleToTotal():Number {
			if(tf.maxScrollV==1){
				return 1;
			}else{
				var visible:Number=tf.bottomScrollV-tf.scrollV+1;
				var total:Number=tf.maxScrollV+visible-1;
				return visible/total;
			}
		}
		
		public static function forInstance(tf:TextField):MLTextField {
			if(servicedObjects[tf]){
				return servicedObjects[tf];
			}else{
				servicedObjects[tf]=new MLTextField(null, tf);
				return servicedObjects[tf];
			}
		}
		
		public function getGotFocus():Boolean {
			return gotFocus;
		}
		
		protected function rememberSelection():void {
			lastTextBeforeInput=tf.text;
			lastCaretIndexBeforeInput=tf.caretIndex;
			lastSelectionBeginIndex=tf.selectionBeginIndex;
			lastSelectionEndIndex=tf.selectionEndIndex;
		}
		
//********************************************************************************************
//		events for MLTextField
//********************************************************************************************
		protected function onTextFieldChange(event:Event):void {
			broadcastEvent(event_Change, event);
			if(!isInput){
				rememberSelection();
			}
		}
		
		protected function onKeyDownWhenFocus(event:Event):void {
			rememberSelection();
		}
		
		protected function onKeyUpWhenFocus(event:Event):void {
			rememberSelection();
			broadcastEvent(event_KeyUpWhenFocus);
		}
		
		protected function onTextFieldLink(event:TextEvent):void {
			broadcastEvent(event_Link, event);
			LinksFromMLTextFieldGlobalBroadcaster.getInstance().broadcastLinkEvent(event);
		}
		
		protected function onTextFieldScroll(event:Event):void {
			broadcastEvent(event_Scroll, event);			broadcastEvent(event_ScrollChanged);
		}
		
		protected function onTextFieldTextInput(event:TextEvent):void {
			isInput=true;
			broadcastEvent(event_BeforeTextInput, event);
			lastInput=event.text;
			rememberSelection();
			tf.addEventListener(Event.CHANGE, onTFChangeAfterInput);
		}
		
		protected function onTFChangeAfterInput(event:Event):void {
			isInput=false;
			lastCaretIndex=tf.caretIndex;
			tf.removeEventListener(Event.CHANGE, onTFChangeAfterInput);
			broadcastEvent(event_AfterTextInput, event);
		}
		
		
		protected function onTextFieldFocusIn(event:FocusEvent):void {
			lastCaretIndex=tf.caretIndex;
			gotFocus=true;
			broadcastEvent(event_FocusIn, event);
			ms.addEventListener(ms.event_Down, onMouseDownWhenFocus);
			ms.addEventListener(ms.event_Up, onMouseUpWhenFocus);
			km.addEventListener(km.event_Down, onKeyDownWhenFocus);			km.addEventListener(km.event_Up, onKeyUpWhenFocus);
		}
		
		protected function onTextFieldFocusOut(event:FocusEvent):void {;
			lastCaretIndex=tf.caretIndex;
			ms.removeEventListener(ms.event_Down, onMouseDownWhenFocus);
			ms.removeEventListener(ms.event_Up, onMouseUpWhenFocus);
			km.removeEventListener(km.event_Down, onKeyDownWhenFocus);
			km.removeEventListener(km.event_Up, onKeyUpWhenFocus);
			gotFocus=false;
			broadcastEvent(event_FocusOut, event);
		}
		
		protected function onTextFieldMouseFocusChange(event:FocusEvent):void {
			broadcastEvent(event_MouseFocusChange, event);
		}
		
		protected function onMouseUpWhenFocus(event:MLEvent):void {
			rememberSelection();
			broadcastEvent(event_MouseUpWhenFocus, event);
			if(tf.selectionBeginIndex>-1 && tf.selectionEndIndex>tf.selectionBeginIndex){
				broadcastEvent(event_FinishSelectingText, event);
			}
		}
		
		protected function onMouseDownWhenFocus(event:MLEvent):void {
			lastCaretIndex=tf.caretIndex;
		}
		
	}
}

internal class Private{}